// Reflect对象
const { log } = console
const obj = {
    name: 'zlf',
    age: '10'
}

log(Reflect.has(obj, 'name')) // true
// Reflect.deleteProperty(obj, 'age') // delete obj.age
// log(obj) // { name: 'zlf' }
// log(Reflect.ownKeys(obj)) // [ 'name', 'age' ]


// 构造函数 创建 实例对象
function Person(name) {
    this.name = name
}
Person.prototype.say = function () {
    log(`hi,my name is ${this.name}`)
}
const p = new Person('Lucky')
// p.say() // hi,my name is Lucky

// class 关键词 创建 实例对象
class Person1 {
    constructor(name) {
        this.name = name
    }
    say() {
        log(`hi,my name is ${this.name}..`)
    }
    static create(name) {
        return new Person1(name)
    }
}
const p1 = new Person1('Tom')
// p1.say() // hi,my name is Tom..

// static 方法 是挂载到当前的类上的，所以里面的 this 不是指向实例对象的，而是当前的类。
const p2 = Person1.create('tom')
log(p2) // Person1 { name: 'tom' }
p2.say() // hi,my name is tom..

// extends 关键字 类的继承
class Student extends Person{
    constructor(name,number){
        super(name)
        this.number = number
    }
    hello(){
        super.say()
        log(`my school number is ${this.number}`)
    }
}
const s = new Student('Jack','10')
log(s.hello()) 
// hi,my name is Jack
// my school number is 10

// Set 类
// 里面的值是不允许重复的,多用于去重。
// - add 方法，返回的是Set对象，可以链式调用，
// - delete 方法，返回 Boolean，Set对象中有这个值，返回true；没有，返回false
// - has 方法，返回 Boolean，Set对象中有这个值，返回true；没有，返回false
// - size 方法，返回Number长度，类似length
let s1 = new Set()
// Set 对象的add方法返回的是Set对象，所以可以链式调用
s1.add(1).add(3).add(4).add(3)
log(s1) // => Set { 1, 3, 4 }
log(s1.size) // => 3
log(s1.has(4)) // => true
log(s1.delete(2)) // => false 

// Set 去重
const list = [1,2,3,4,2,3,1]
const setObj = new Set(list)
log(setObj) // => Set { 1, 2, 3, 4 }
const setList = [...setObj]
const setList2 = Array.from(setObj)
log(setList) // => [ 1, 2, 3, 4 ]
log(setList2) // => [ 1, 2, 3, 4 ]

// Map 对象 严格意义上的键值对集合
// es6之前,对象的键值对,会把所有的key都转化成string类型
const object2 = {}
object2[true] = '12'
object2[123] = '123'
object2[{a:1}] = '34'
log(object2) // => { '123': '123', 'true': '12', '[object Object]': '34' }
log(Object.keys(object2)) // => [ '123', 'true', '[object Object]' ]
log(object2[{}]) // => 34
log(object2['[object Object]']) // => 34
// es6之后，Map 的运用
const map1 = new Map()
const tom = {name:'tom'}
map1.set(tom,90)
map1.set('age', 20)
log(map1) // => Map { { name: 'tom' } => 90, 'age' => 20 }
log(map1.get('age')) // => 20
log(map1.get(tom)) // => 90
log(map1.has(tom)) // => true
// log(map1.delete('age')) // => true
// log(map1.clear()) // undefined
map1.forEach((value,key)=>{
    log(value,key) // => 90 { name: 'tom' }  20 age
})

// Symbol 
log(Symbol()===Symbol()) // => false
const object3 = {}
object3[Symbol()] = '123'
object3[Symbol()] = '234'
// log(object3) // => { [Symbol()]: '123', [Symbol()]: '234' }
// 私有属性
const name = Symbol()
const object4 = {
    [name]: 'tom',
    say(){
        log(this[name])
    }
}
// object4[Symbol()] 
object4.say() // => tom
log(object4[name]) // => tom

// for...of 遍历, item 是数组中的元素。
// for...in,item 是数组中的下标（对象中的key）
// for循环h和for...of,for...in，用break可以退出循环，forEach不能跳出循环。
const arr = [100,200,300,400]
for(let item of arr){
    log(item) // 100 200 300
    if(item >200){ // 300 > 200
        break
    }
}
// 不可遍历 对象
// for (let item of obj){
//     log(item) // => TypeError: object4 is not iterable
// }
// for...of，可以遍历任何数组类型
// 可以遍历 Set 对象
const s2 = new Set(arr)
for (let item of s2){
    log(item) // => // 100 200 300 400
}
// 可以遍历 Map 对象
const map2 = new Map()
map2.set('name','zlf')
map2.set('age','10')
for (let item of map2){
    log(item) // => // [ 'name', 'zlf' ]，[ 'age', '10' ]
}

// iterator 迭代器: 
// 下面过程就是for...of遍历的原理
const set1 = new Set(['foo','bar','baz'])
const iterator = set1[Symbol.iterator]()

log(iterator.next())
log(iterator.next())
log(iterator.next())
log(iterator.next())
log(iterator.next())
// = > { value: 'foo', done: false }
// { value: 'bar', done: false }
// { value: 'baz', done: false }
// { value: undefined, done: true }
// { value: undefined, done: true }

// 实现迭代器接口 Iterable
// 为什么要实现对象的迭代器接口：不用关心对象内部的元素，在外部直接进行遍历
const obj1 = {
    life:['吃饭','睡觉','打豆豆'],
    study:['语文','数学','科学'],
    work:['上班'],
    each:function(){
        let arr = [...this.life,...this.study,...this.work]
        // arr.forEach(item=>{
        //     log(item)
        // })
        for(const item of arr){
            log(item)
        }
    },
    [Symbol.iterator]:function(){
        let arr = [...this.life,...this.study,...this.work]
        let index = 0
        return {
            next: function(){
                return{
                    value:arr[index],
                    done: index ++ >= arr.length
                }
            }
        }
    }
}
// obj1.each() // '吃饭','睡觉','打豆豆','语文','数学','科学','上班'
for(const item of obj1){
    log(item) // '吃饭','睡觉','打豆豆','语文','数学','科学','上班'
}
// 上面两种方法的效果一样，但是迭代器接口的方法的实现，让数据遍历变得统一。


// Generator 生成器函数
// 生成器函数 返回的是个生成器对象
// 生成器对象有和迭代器一样的特性，有个next方法，返回的是value和done标识 组成的对象
function * generator1(){
    log('zlf')
    return 100
}
const res = generator1()
log(res) // => Object [Generator] {}
log(res.next()) // => zlf { value: 100, done: true }
// 惰性执行
function * generator2(){
    log('1111')
    yield 100
    log('2222')
    yield 200
    log('3333')
    yield 300
}
const res2 = generator2()
log(res2.next()) // => 1111 { value: 100, done: false }
log(res2.next()) // => 2222 { value: 200, done: false }
log(res2.next()) // => 3333 { value: 300, done: false }
log(res2.next()) // => { value: undefined, done: true }

// 生成器应用
// 1.实现发号器
function * generator3(){
    let index = 0
    while(true){
        yield index++
        // log(index)
    }
}
const idMaker = generator3()
log(idMaker.next().value) // 0
log(idMaker.next().value) // 1
log(idMaker.next().value) // 2
log(idMaker.next().value) // 3
log(idMaker.next().value) // 4

// 2. 使用生成器Generator 实现 迭代器iterator 方法
const todos = {
    life:['吃饭','睡觉','打豆豆'],
    study:['语文','数学','科学'],
    work:['上班'],
    [Symbol.iterator]:function * (){
        let arr = [...this.life,...this.study,...this.work]
        let index = 0
        for(let item of arr){
            yield item
        }
    }
}
for(const item of todos){
    log(item)
}

// ES2017
// Object.entries
// 对象--`Object.entries`--> 双层数组 --`new Map()`--> Map对象 --`for...of`--> 键值对数组 或 键+值
const object5 = {
    name:'tom',
    age : '20'
}
log(Object.entries(object5)) // => [ [ 'name', 'tom' ], [ 'age', '20' ] ]
for(const [key,value] of Object.entries(object5)){
    log(key,value) // => name tom  age 20
}

const map3 = new Map()
map3.set('name','Tom')
map3.set('age','100')
log(map3) // => Map { 'name' => 'Tom', 'age' => '100' }
const mapObj = new Map(Object.entries(object5))
log(mapObj) // => Map { 'name' => 'tom', 'age' => '20' }
// 遍历出一层
for(const item of mapObj){
    log(item) // 遍历出一层 [ 'name', 'tom' ] [ 'age', '20' ]
}
// 全部遍历出来
for(const [key,value] of mapObj){
    log(key,value) // 遍历'name', 'tom' 'age', '20' 
}

// Object.getOwnPropertyDescriptors():用来获取一个对象的所有自身属性的描述符
const pd1 = {
    firstName:'Lei',
    lastName:'Li',
    get fullName(){
        return this.firstName + ' ' + this.lastName
    }
}
log(pd1.fullName) // => Lei Li
// 出现问题：
// const pd2 =  Object.assign({},pd1)
// pd2.firstName = 'nin'
// log(pd2.fullName) // => Lei Li ：并未改变

// Object.getOwnPropertyDescriptors()解决问题
const desc = Object.getOwnPropertyDescriptors(pd1)
// log(desc)
/* => 
{
  firstName: {
    value: 'Lei',
    writable: true,
    enumerable: true,
    configurable: true
  },
  lastName: { value: 'Li', writable: true, enumerable: true, configurable: true },
  fullName: {
    get: [Function: get fullName],
    set: undefined,
    enumerable: true,
    configurable: true
  }
}
*/
const pd2 = Object.defineProperties({},desc) // 这里不能用assign,Object.defineProperties()直接在一个对象上定义新的属性或修改现有属性，并返回该对象。
pd2.firstName = 'nin'
log(pd2.fullName) // => nin Li

// padEnd字符串填充 
const object6 = {
    http: '121',
    css:'11',
    javascript:'1'
}
for(const [name,val] of Object.entries(object6)){
    // log(name,val)
    log(`${name.padEnd(16,'-')}|${val.padStart(4,'-')}`)
}
/*
http------------|-121
css-------------|--11
javascript------|---1
*/

// 在函数参数中添加尾逗号，方便编程，方便修改
function name11(
    bar,
    foo,
){
    // todo
}
// async/await
// 后面课程介绍
